package mosdi.paa.apps;

import java.util.Arrays;

import mosdi.fa.Partition;
import mosdi.paa.MinimizableDAA;
import mosdi.util.Alphabet;
import mosdi.util.CodonAlphabet;
import mosdi.util.ProductEncoder;

/** DAA over the codon alphabet that counts the number of CGs that are not 
 *  under coding contraints, that is, the number of CGs that could silently (without
 *  changing amino acid sequence) mutate into TG or CA. In fact that means that
 *  all CGs appearing in frame 1 or 2 are counted. */
public class UnconstrainedCpGDAA extends MinimizableDAA {

	private final Alphabet dnaAlphabet = Alphabet.getDnaAlphabet();
	private int maxCount;
	// first component: values 0/1: previous codon didnt/did end on C
	// second component: current codon
	private ProductEncoder stateEncoder;
	private boolean countFrame1CpGs;
	
	public UnconstrainedCpGDAA(int maxCount) {
		this.maxCount = maxCount;
		stateEncoder = new ProductEncoder(true, 2, CodonAlphabet.size());
		countFrame1CpGs = true;
	}

	public UnconstrainedCpGDAA(int maxCount, boolean countFrame1CpGs) {
		this.maxCount = maxCount;
		stateEncoder = new ProductEncoder(true, 2, CodonAlphabet.size());
		this.countFrame1CpGs = countFrame1CpGs;
	}

	@Override
	public Partition getStatePartition() {
		Integer[] allEmissions = new Integer[getStateCount()];
		for (int i=0; i<getStateCount(); ++i) {
			allEmissions[i] = getEmission(i);
		}
		return new Partition(Arrays.asList(allEmissions));
	}

	@Override
	public int getAlphabetSize() {
		return CodonAlphabet.size();
	}

	@Override
	public int getStartState() {
		return stateEncoder.encode(0, CodonAlphabet.getIndex("AAA"));
	}

	@Override
	public int getStateCount() {
		return stateEncoder.getValueCount();
	}

	@Override
	public int getTransitionTarget(int state, int character) {
		int[] decodedState = stateEncoder.decode(state);
		decodedState[0] = dnaAlphabet.getIndex('C')==CodonAlphabet.get(decodedState[1])[2]?1:0;
		decodedState[1] = character;
		return stateEncoder.encode(decodedState);
	}

	@Override
	public int getEmission(int state) {
		int[] decodedState = stateEncoder.decode(state);
		int[] codon = CodonAlphabet.get(decodedState[1]);
		int result = 0;
		// increment if last codon ended on C and current starts with G
		if ((decodedState[0]==1) && (codon[0]==dnaAlphabet.getIndex('G'))) result += 1;
		if (countFrame1CpGs) {
			// increment if current codon ends on CG
			if ((codon[1]==dnaAlphabet.getIndex('C')) && (codon[2]==dnaAlphabet.getIndex('G'))) result += 1;
		}
		return result;
	}

	@Override
	public int getEmissionCount() {
		return 3;
	}

	@Override
	public int getStartValue() {
		return 0;
	}

	@Override
	public int getValueCount() {
		return maxCount+1;
	}

	@Override
	public int performOperation(int state, int value, int emission) {
		return Math.min(maxCount, value+emission);
	}

}
